/*
 * File: vfsdriver.c
 *
 * Description: This is a driver program for testing your VFS system using an interaction script as input
 * You need to make additional calls to your respective functions as noted in the comments below
 * Make sure the output you display is exactly as per the given specifications for you. Do NOT print
 * any extra output (like debug messages) in the final version of your driver program. You can use this driver program
 * in a in incremental manner as you keep implementing one operator after another. For operators not yet implemented,
 * you can leave the output as given ("TO_BE_DONE"). So you can use this program as your "main" program for testing purposes.
 *
 * DO NOT write the full code for operators in the driver program! You must only CALL your functions from here.
 *
 * Usage: vfsdriver <scriptfilename>
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#include"../include/global_header.h"

#define BUFSIZE 200
#define CMDSIZE 30
#define PARSIZE 100

void createvfs ( char *P1, int P2 );
void mountvfs ( char *P1 );
void unmountvfs ( char *P1 );
void makedir ( char *P1, char *P2 );
void deletedir ( char *P1 );
void movedir ( char *P1, char *P2 );
void listdir ( char *P1, int P2, char *P3 );
void addfile ( char *P1, char *P2, char *P3 );
void listfile ( char *P1, char *P2 );
void updatefile ( char *P1, char *P2 );
void removefile ( char *P1 );
void movefile ( char *P1, char *P2 );
void copyfile ( char *P1, char *P2 );
void exportfile ( char *P1, char *P2 );
void searchfile ( char *P1, char *P2 );

void processcommand( char *command, char *P1, char *P2, char *P3 );
int valid_filename(char* name);

int main( int argc, char *argv[] )
{
	FILE *scriptfp;
	char linebuffer[BUFSIZE];
	char command[CMDSIZE], par1[PARSIZE], par2[PARSIZE], par3[PARSIZE];
	char *token;

	if( argc != 2 ){
		fprintf(stderr,"Usage: vfsdriver <scriptfile>\n");
		return(1);
	}

	if( (scriptfp=fopen(argv[1],"r")) == NULL ){
		fprintf(stderr,"Unable to open script file: %s\n", argv[1]);
		return(2);
	}

	while( fgets(linebuffer, sizeof(linebuffer), scriptfp) != NULL ){
		/* This output is for debugging... do not uncomment in final version */
		/*
		printf("==================================================\n");
		printf("Processing: %s", linebuffer);
		printf("==================================================\n");
		*/

		/* Remove the extra newline character in the end of line */
		linebuffer[ strlen(linebuffer)-1 ] = '\0';

		/* Get the command and the parameters using tokenizer */
		strcpy( command, (token = strtok(linebuffer, " ")) == NULL ? "" : token );

		strcpy( par1, (token = strtok(NULL, " ")) == NULL ? "" : token );
		strcpy( par2, (token = strtok(NULL, " ")) == NULL ? "" : token );
		strcpy( par3, (token = strtok(NULL, " ")) == NULL ? "" : token );
		/* printf("Command:%s:p1:%s:p2:%s:p3:%s\n",command, par1, par2, par3); */

		processcommand( command, par1, par2, par3 );
	}
}

void processcommand( char *command, char *P1, char *P2, char *P3 )
{
	if( strcmp(command, "createvfs") == 0 ){
		int size = atoi(P2);
		createvfs (P1,size);
	}
	else if( strcmp(command, "mountvfs") == 0 )
		mountvfs (P1);
	else if( strcmp(command, "unmountvfs") == 0 )
		unmountvfs (P1);
	else if( strcmp(command, "makedir") == 0 )
		makedir (P1,P2);
	else if( strcmp(command, "deletedir") == 0 )
		deletedir (P1);
	else if( strcmp(command, "movedir") == 0 )
		movedir (P1,P2);
	else if( strcmp(command, "listdir") == 0 ){
		int flag = atoi(P2);
		listdir (P1,flag,P3);
	}
	else if( strcmp(command, "addfile") == 0 )
		addfile (P1,P2,P3);
	else if( strcmp(command, "listfile") == 0 )
		listfile (P1,P2);
	else if( strcmp(command, "updatefile") == 0 )
		updatefile (P1,P2);
	else if( strcmp(command, "removefile") == 0 )
		removefile (P1);
	else if( strcmp(command, "movefile") == 0 )
		movefile (P1,P2);
	else if( strcmp(command, "copyfile") == 0 )
		copyfile (P1,P2);
	else if( strcmp(command, "exportfile") == 0 )
		exportfile (P1,P2);
	else if( strcmp(command, "searchfile") == 0 )
		searchfile (P1,P2);
	else
		printf("Ignoring invalid command %s\n", command);
}

void createvfs ( char *name, int size )
{
	 int i;

     // Validate name
     if(name == NULL || strlen(name) <= 0 || strcmp(name, " ") == 0 || size == NULL){
        printf("%s\n",  ERR_VFS_CREATE_00 );    // VFS_INSUFFICIENT_ARGUMENTS
        return;
     }
     
     if(is_vfsmounted == 1) {
        printf("%s\n", ERR_VFS_CREATE_02);      // CANNOT_CREATE_DATAFILE
        return;     
     }

     if(!valid_filename(name)){
        printf("%s\n", ERR_VFS_CREATE_03);      // INVALID_CHARACTER_IN_NAME
        return;
     }

     if(strlen(name) > 30){
        printf("%s\n", ERR_VFS_CREATE_05);      // VFS_LABEL_TOO_LARGE
        return;
     }

     // Validate size
     if(size < 1 || size > 1024){
        printf("%s\n",ERR_VFS_CREATE_04);       // INVALID_SIZE
        return;
     }
     
     if(strcmp(vfs_name,name) == 0) {
        printf("%s\n",ERR_VFS_CREATE_01);       // DATA_FILE_ALREADY_EXISTS
        return;   
     }

     // Perform operation.
     create_vfs(name,size);
}

void mountvfs ( char *name )
{
    // Validate name
     if(name == NULL || strlen(name) <= 0){
        printf("%s\n", ERR_VFS_MOUNT_00);       // VFS_INSUFFICIENT_ARGUMENTS
        return;
     }

     if(is_vfsmounted == 1 && strcmp(name, vfs_name) != 0){
        printf("%s\n", ERR_VFS_MOUNT_03);       // VFS_ALREADY_MOUNTED
        return;
     }

     if(is_vfsmounted == 1 && strcmp(name, vfs_name) == 0){
        printf("%s\n", ERR_VFS_MOUNT_03);       // VFS_ALREADY_MOUNTED
        return;
     }

     mount_vfs(name);
}

void unmountvfs ( char *name )
{
    if(name == NULL || strlen(name) <= 0){
        printf("%s\n", ERR_VFS_UNMOUNT_00);     // VFS_INSUFFICIENT_ARGUMENTS
        return;
    }

    if(is_vfsmounted != 1){
        printf("%s\n",ERR_VFS_UNMOUNT_03);      // VFS_NOT_MOUNTED
        return;
    }

    if(is_vfsmounted == 1 && (strcmp(name, vfs_name) != 0)){
        printf("%s\n",ERR_VFS_UNMOUNT_03);      // VFS_NOT_MOUNTED
        return;
    }

    unmount_vfs(name);
}

void makedir ( char *parent_path, char *dir_name )
{
     if(is_vfsmounted != 1){
        printf("%s\n", ERR_VFS_MAKEDIR_05);     // VFS_NOT_MOUNTED
        return;
     }

     if(total_fdcount == ((total_size * 1024)/BLOCK_SIZE)) {
        printf("%s\n",ERR_VFS_MAKEDIR_01);                  // FILESYSTEM_FULL
        return;
     }

     // Validate name
     if(dir_name == NULL || strlen(dir_name) <= 0 || strcmp(dir_name, " ") == 0){
        printf("%s\n", ERR_VFS_MAKEDIR_00);     // VFS_INSUFFICIENT_ARGUMENTS
        return;
     }

     if(!valid_filename(dir_name)){
        printf("%s\n", ERR_VFS_MAKEDIR_02);     // INVALID_CHARACTER_IN_DIRNAME
        return;
     }

     write_to_file(-1,parent_path,dir_name,NULL,'d');
}

void deletedir ( char *dir_path )
{
    if(is_vfsmounted != 1){
        printf("%s\n",  ERR_VFS_DELETEDIR_04);      // VFS_NOT_MOUNTED
        return;
     }

     // Validate name
     if(dir_path == NULL || strlen(dir_path) <= 0 ||
        strcmp(dir_path, " ") == 0 ){

            printf("%s\n", ERR_VFS_DELETEDIR_00);       // "VFS_INSUFFICIENT_ARGUMENTS"
            return;
    }

    delete_dir(dir_path);	
}

void movedir ( char *src_path, char *dest_path )
{
    if(is_vfsmounted != 1) {
        printf("%s\n", ERR_VFS_MOVEFILE_06);
        return;
    }

    if(src_path == NULL || strlen(src_path) <= 0 || strcmp(src_path, " ") == 0){
        printf("%s\n", ERR_VFS_MOVEDIR_00);     // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }

    if(dest_path == NULL || strlen(dest_path) <= 0 || strcmp(dest_path, " ") == 0){
        printf("%s\n", ERR_VFS_MOVEDIR_00);     // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }

    move_directory(src_path, dest_path);
}

// P1 - Path of which to be listed.
// P2 - flag for recursive or not.
// 1=recursive , 0=Non-recursive
// P3 - file path of where to store the listed data.
void listdir ( char *directory_path, int rec_flag, char *output_filepath )
{
    if(is_vfsmounted != 1) {
        printf("%s\n", ERR_VFS_LISTDIR_03);         // "VFS_NOT_MOUNTED"
        return;
    }

    if((directory_path==NULL)|| (strlen(directory_path) <= 0) || (strcmp(directory_path, " ") == 0)){
        printf("%s\n",ERR_VFS_LISTDIR_00);          // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }

    if((output_filepath==NULL)|| (strlen(output_filepath) <= 0) || (strcmp(output_filepath, " ") == 0)){
        printf("%s\n",ERR_VFS_LISTDIR_00);          // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }

    if(rec_flag != 0 && rec_flag != 1){
        printf("%s\n", ERR_VFS_LISTDIR_02);         // "INVALID_FLAG"
        return;
    }

    ls(directory_path,rec_flag,output_filepath);
}

// Same as create file
void addfile ( char *dest_dir_name, char *file_name, char *data)
{
    if(is_vfsmounted == 0){
        printf("%s\n", ERR_VFS_ADDFILE_07);      // "VFS_NOT_MOUNTED"
        return;
    }

    if((dest_dir_name==NULL)||strlen(dest_dir_name) <= 0 || (strcmp(dest_dir_name, " ") == 0)){
        printf("%s\n",ERR_VFS_ADDFILE_00);      // VFS_INSUFFICIENT_ARGUMENTS
        return;
    }

    if((file_name==NULL)||strlen(file_name) <= 0 || (strcmp(file_name, " ") == 0)){
        printf("%s\n",ERR_VFS_ADDFILE_00);      // VFS_INSUFFICIENT_ARGUMENTS
        return;
    }
    if((data==NULL)||strlen(data) <= 0 || (strcmp(data, " ") == 0)){
        printf("%s\n",ERR_VFS_ADDFILE_00);      // VFS_INSUFFICIENT_ARGUMENTS
        return;
    }

    if(valid_filename(file_name) != 1){
        printf("%s\n", ERR_VFS_ADDFILE_02);     // "INVALID_CHARACTER_IN_FILENAME"
        return;
    }

    long int block_no = get_free_block();
    if(block_no == -1) {
        printf("%s\n", ERR_VFS_ADDFILE_07);      // "VFS_NOT_MOUNTED"
        return;
    }

    add_file(file_name, dest_dir_name, data, block_no);


}

void listfile ( char *file_path, char *output_file )
{
    if(is_vfsmounted != 1){
        printf("%s\n", ERR_VFS_LISTFILE_04);      // "VFS_NOT_MOUNTED"
        return;
    }

    if((file_path==NULL)||strlen(file_path) <= 0 || (strcmp(file_path, " ") == 0)){
        printf("%s\n",ERR_VFS_LISTFILE_00);      // VFS_INSUFFICIENT_ARGUMENTS
        return;
    }

    if((output_file==NULL)||strlen(output_file) <= 0 || (strcmp(output_file, " ") == 0)){
        printf("%s\n",ERR_VFS_LISTFILE_00);      // VFS_INSUFFICIENT_ARGUMENTS
        return;
    }

	FILE* fp = NULL;
	if((fp = fopen(output_file,"w")) == NULL) {
		printf("%s\n",ERR_VFS_LISTFILE_03); // "CANNOT_CREATE_OUTPUTFILE"
		return;
	}
	if(fp != NULL) fclose(fp);

    export_binary_file(file_path, output_file, 0);   // 0 for listfile and 1 for exportfile
}

void updatefile ( char *input_path, char *output_path )
{
    if(is_vfsmounted != 1){
        printf("%s\n", ERR_VFS_UPDATEFILE_04); // "VFS_NOT_MOUNTED"
        return;
    }
    if((input_path==NULL)||strlen(input_path) <= 0 || (strcmp(input_path, " ") == 0)){
        printf("%s\n",ERR_VFS_UPDATEFILE_00); // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }
    if((output_path==NULL)||strlen(output_path) <= 0 || (strcmp(output_path, " ") == 0)){
        printf("%s\n",ERR_VFS_UPDATEFILE_00); // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }
    update_file(input_path, output_path);
}

void removefile ( char *file_path )
{
    if(is_vfsmounted != 1){
        printf("%s\n", ERR_VFS_REMOVEFILE_02); // "VFS_NOT_MOUNTED"
        return;
    }

    if((file_path==NULL)||strlen(file_path) <= 0 || (strcmp(file_path, " ") == 0)){
        printf("%s\n",ERR_VFS_REMOVEFILE_00); // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }

    delete_file(file_path);
}

void movefile ( char *src_path, char *dest_path )
{
    if(is_vfsmounted != 1) {
        printf("%s\n", ERR_VFS_MOVEFILE_06);
        return;
    }
    if((src_path==NULL)||strlen(src_path) <= 0 || (strcmp(src_path, " ") == 0)){
        printf("%s\n",ERR_VFS_MOVEFILE_00); // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }
    if((dest_path==NULL)||strlen(dest_path) <= 0 || (strcmp(dest_path, " ") == 0)){
        printf("%s\n",ERR_VFS_MOVEFILE_00); // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }
    move_file(src_path,dest_path);
}

void copyfile (char *src_path, char *dest_path )
{
    if(is_vfsmounted != 1) {
        printf("%s\n", ERR_VFS_COPYFILE_05);
        return;
    }
    if((src_path==NULL)||strlen(src_path) <= 0 || (strcmp(src_path, " ") == 0)){
        printf("%s\n",ERR_VFS_COPYFILE_00); // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }
    if((dest_path==NULL)||strlen(dest_path) <= 0 || (strcmp(dest_path, " ") == 0)){
        printf("%s\n",ERR_VFS_COPYFILE_00); // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }
     copy_file(src_path,dest_path);
}

void exportfile ( char *file_path, char *output_file )
{
    if(is_vfsmounted != 1){
        printf("%s\n", ERR_VFS_EXPORTFILE_04);      // "VFS_NOT_MOUNTED"
        return;
    }

    if((file_path==NULL)||strlen(file_path) <= 0 || (strcmp(file_path, " ") == 0)){
        printf("%s\n",ERR_VFS_EXPORTFILE_00);      // VFS_INSUFFICIENT_ARGUMENTS
        return;
    }

    if((output_file==NULL)||strlen(output_file) <= 0 || (strcmp(output_file, " ") == 0)){
        printf("%s\n",ERR_VFS_EXPORTFILE_00);      // VFS_INSUFFICIENT_ARGUMENTS
        return;
    }  

    export_binary_file(file_path, output_file, 1);   // 0 for listfile and 1 for exportfile
}

void searchfile ( char *input_name, char *output_file )
{
    if(is_vfsmounted != 1) {
        printf("%s\n", ERR_VFS_SEARCHFILE_02);          // "VFS_NOT_MOUNTED"
        return;
    }

    if((input_name==NULL)||strlen(input_name) <= 0 || (strcmp(input_name, " ") == 0)){
        printf("%s\n",ERR_VFS_SEARCHFILE_00);           // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }

    if((output_file==NULL)||strlen(output_file) <= 0 || (strcmp(output_file, " ") == 0)){
        printf("%s\n",ERR_VFS_SEARCHFILE_00);           // "VFS_INSUFFICIENT_ARGUMENTS"
        return;
    }

    search_files(input_name, output_file);
}

// Support functions.
int valid_filename(char* name){
    int i;

    for(i=0; i<strlen(name); i++){
   
        if(name[i] == '/')
        {
            return 0;
        }
    }
    return 1;
}
